import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Quad;


public class Renderer {
	
	static boolean physicsEnabled = false;
	static int blockSize = 1;
	
	public static void renderChunkMatrix()
	{
		for(Chunk c : World.activeChunks)//chunk
		{
			c.visibility = new BlockVisibility[Chunk.width][Chunk.height][Chunk.depth];
			for(int x = 0; x < c.chunk.length; x++)//x position
			{
				for(int y = 0; y < c.chunk[0].length; y++)//y position
				{
					for(int z = 0; z < c.chunk[0][0].length; z++)//z position
					{
						computeVisibility(c, x, y, z);
						drawBlock(c, x, y, z);
					}
				}
			}
		}
	}
	
	public static void initializeScene()
	{
		for(Chunk c : World.activeChunks)
		{
			findOctreeVisibility(c.octree);
			drawOctree(c.octree);
		}
	}
	
	
	public static void findOctreeVisibility(Octree o)
	{
		if(o != null)
		{
			if(o.type == 0)
			{
				o.setVisible(false);
			}
			else if(o.type == -1)
			{
				for(Octree child : o.child)
					findOctreeVisibility(child);//add functionality to check adjacent octrees
			}
			else
			{
				o.setVisible(true);
			}
		}
	}
	
	public static void drawOctree(Octree o)
	{
		if(o != null && o.type != BlockType.AIR)
		{
			if(o.type == -1)//check children for block info
			{
				for(Octree child : o.child)
				{
					drawOctree(child);
				}
			}
			else//draw block
			{
				Quad quad = null;
				Quaternion q = new Quaternion();
				
				//north face
				if(Engine.getEngine().getWorld().getWorldNode().getChild(o.chunkId + ":" + o.offsetX + ":" + o.offsetY + ":" + o.offsetZ + "N") == null)
				{
					quad = new Quad(blockSize * o.getOffset(o), blockSize * o.getOffset(o));
					
					//Geometry
					Geometry face = new Geometry(o.chunkId + ":" + o.offsetX + ":" + o.offsetY + ":" + o.offsetZ + "N", quad);
					face.setMaterial(Materials.getMaterial((int)o.type));
					face.setLocalTranslation(new Vector3f(o.offsetX, o.offsetY, o.offsetZ));
					face.setUserData("x", o.offsetX);
					face.setUserData("y", o.offsetY);
					face.setUserData("z", o.offsetZ);
					face.setUserData("chunk", o.chunkId);
					face.setUserData("face", "north");
					Engine.getEngine().getWorld().getWorldNode().attachChild(face);
					
					//Physics
				    if(physicsEnabled)
				    {
				    	face.addControl(new RigidBodyControl(0f));
					    Engine.getEngine().getStateManager().getState(BulletAppState.class).getPhysicsSpace().add(face);
				    }
				}
				
				//east
				if(Engine.getEngine().getWorld().getWorldNode().getChild(o.chunkId + ":" + o.offsetX + ":" + o.offsetY + ":" + o.offsetZ + "E") == null)
				{
					q.fromAngleAxis(-FastMath.PI/2, new Vector3f(0, 1, 0));
					quad = new Quad(blockSize * o.getOffset(o), blockSize * o.getOffset(o));
					
					//Geometry
					Geometry face = new Geometry(o.chunkId + ":" + o.offsetX + ":" + o.offsetY + ":" + o.offsetZ + "E", quad);
					face.setMaterial(Materials.getMaterial((int)o.type));
					face.setLocalRotation(q);
					face.setLocalTranslation(new Vector3f(o.offsetX, o.offsetY, o.offsetZ - (blockSize * o.getOffset(o))));
					face.setUserData("x", o.offsetX);
					face.setUserData("y", o.offsetY);
					face.setUserData("z", o.offsetZ);
					face.setUserData("chunk", o.chunkId);
					face.setUserData("face", "north");
					Engine.getEngine().getWorld().getWorldNode().attachChild(face);
					
					//Physics
				    if(physicsEnabled)
				    {
				    	face.addControl(new RigidBodyControl(0f));
					    Engine.getEngine().getStateManager().getState(BulletAppState.class).getPhysicsSpace().add(face);
				    }
				}
				
				//south
				if(Engine.getEngine().getWorld().getWorldNode().getChild(o.chunkId + ":" + o.offsetX + ":" + o.offsetY + ":" + o.offsetZ + "S") == null)
				{
					q.fromAngleAxis(FastMath.PI, new Vector3f(0, 1, 0));
					quad = new Quad(blockSize * o.getOffset(o), blockSize * o.getOffset(o));
					//Geometry
					Geometry face = new Geometry(o.chunkId + ":" + o.offsetX + ":" + o.offsetY + ":" + o.offsetZ + "S", quad);
					face.setMaterial(Materials.getMaterial((int)o.type));
					face.setLocalRotation(q);
					face.setLocalTranslation(new Vector3f(o.offsetX + (blockSize * o.getOffset(o)), o.offsetY, o.offsetZ - (blockSize * o.getOffset(o))));
					face.setUserData("x", o.offsetX);
					face.setUserData("y", o.offsetY);
					face.setUserData("z", o.offsetZ);
					face.setUserData("chunk", o.chunkId);
					face.setUserData("face", "north");
					Engine.getEngine().getWorld().getWorldNode().attachChild(face);
					
					//Physics
				    if(physicsEnabled)
				    {
				    	face.addControl(new RigidBodyControl(0f));
					    Engine.getEngine().getStateManager().getState(BulletAppState.class).getPhysicsSpace().add(face);
				    }
				}
				
				//west
				if(Engine.getEngine().getWorld().getWorldNode().getChild(o.chunkId + ":" + o.offsetX + ":" + o.offsetY + ":" + o.offsetZ + "W") == null)
				{
					q.fromAngleAxis(FastMath.PI/2, new Vector3f(0, 1, 0));
					quad = new Quad(blockSize * o.getOffset(o), blockSize * o.getOffset(o));
					
					//Geometry
					Geometry face = new Geometry(o.chunkId + ":" + o.offsetX + ":" + o.offsetY + ":" + o.offsetZ + "W", quad);
					face.setMaterial(Materials.getMaterial((int)o.type));
					face.setLocalRotation(q);
					face.setLocalTranslation(new Vector3f(o.offsetX + (blockSize * o.getOffset(o)), o.offsetY, o.offsetZ));
					face.setUserData("x", o.offsetX);
					face.setUserData("y", o.offsetY);
					face.setUserData("z", o.offsetZ);
					face.setUserData("chunk", o.chunkId);
					face.setUserData("face", "north");
					Engine.getEngine().getWorld().getWorldNode().attachChild(face);
					
					//Physics
				    if(physicsEnabled)
				    {
				    	face.addControl(new RigidBodyControl(0f));
					    Engine.getEngine().getStateManager().getState(BulletAppState.class).getPhysicsSpace().add(face);
				    }
				}
				
				//top
				if(Engine.getEngine().getWorld().getWorldNode().getChild(o.chunkId + ":" + o.offsetX + ":" + o.offsetY + ":" + o.offsetZ + "T") == null)
				{
					q.fromAngleAxis(3 * FastMath.PI / 2, new Vector3f(1, 0, 0));
					quad = new Quad(blockSize * o.getOffset(o), blockSize * o.getOffset(o));
					//Geometry
					Geometry face = new Geometry(o.chunkId + ":" + o.offsetX + ":" + o.offsetY + ":" + o.offsetZ + "T", quad);
					face.setMaterial(Materials.getMaterial((int)o.type));
					face.setLocalRotation(q);
					face.setLocalTranslation(new Vector3f(o.offsetX, o.offsetY + (blockSize * o.getOffset(o)), o.offsetZ));
					face.setUserData("x", o.offsetX);
					face.setUserData("y", o.offsetY);
					face.setUserData("z", o.offsetZ);
					face.setUserData("chunk", o.chunkId);
					face.setUserData("face", "north");
					Engine.getEngine().getWorld().getWorldNode().attachChild(face);
					
					//Physics
				    if(physicsEnabled)
				    {
				    	face.addControl(new RigidBodyControl(0f));
					    Engine.getEngine().getStateManager().getState(BulletAppState.class).getPhysicsSpace().add(face);
				    }
				}
				
				//bottom
				if(Engine.getEngine().getWorld().getWorldNode().getChild(o.chunkId + ":" + o.offsetX + ":" + o.offsetY + ":" + o.offsetZ + "B") == null)
				{
					q.fromAngleAxis(FastMath.PI/2, new Vector3f(1, 0, 0));
					quad = new Quad(blockSize * o.getOffset(o), blockSize * o.getOffset(o));
					//Geometry
					Geometry face = new Geometry(o.chunkId + ":" + o.offsetX + ":" + o.offsetY + ":" + o.offsetZ + "B", quad);
					face.setMaterial(Materials.getMaterial((int)o.type));
					face.setLocalRotation(q);
					face.setLocalTranslation(new Vector3f(o.offsetX, o.offsetY, o.offsetZ - (blockSize * o.getOffset(o))));
					face.setUserData("x", o.offsetX);
					face.setUserData("y", o.offsetY);
					face.setUserData("z", o.offsetZ);
					face.setUserData("chunk", o.chunkId);
					face.setUserData("face", "north");
					Engine.getEngine().getWorld().getWorldNode().attachChild(face);
					
					//Physics
				    if(physicsEnabled)
				    {
				    	face.addControl(new RigidBodyControl(0f));
					    Engine.getEngine().getStateManager().getState(BulletAppState.class).getPhysicsSpace().add(face);
				    }
				}
			}
		}
	}
	
	
	//not sure I want this method here...
	public static void computeVisibility(Chunk c, int x, int y, int z)
	{
		checkBounds(c, x, y, z);
		Chunk adjacent;
		c.visibility[x][y][z] = new BlockVisibility();
		if(c.chunk[x][y][z] != BlockType.AIR)
		{
			//WEST
			if(x == 0)
			{
				//check visibility of nearby chunk
				if(World.chunkExists(c.offsetX - Chunk.width, c.offsetZ))
				{
					adjacent = World.getChunk(c.offsetX - Chunk.width, c.offsetZ);
					if(adjacent.chunk[Chunk.width - 1][y][z] == BlockType.AIR)
						c.visibility[x][y][z].east = true;
					else
						c.visibility[x][y][z].east = false;
				}
				else
					c.visibility[x][y][z].east = false;
			}
			else
			{
				if(c.chunk[x - 1][y][z] == BlockType.AIR)
					c.visibility[x][y][z].east = true;
				else
					c.visibility[x][y][z].east = false;
			}
			
			//EAST
			if(x == c.chunk.length - 1)
			{
				//check visibility of nearby chunk
				if(World.chunkExists(c.offsetX + Chunk.width, c.offsetZ))
				{
					adjacent = World.getChunk(c.offsetX + Chunk.width, c.offsetZ);
					if(adjacent.chunk[0][y][z] == BlockType.AIR)
						c.visibility[x][y][z].west = true;
					else
						c.visibility[x][y][z].west = false;
				}
				else
					c.visibility[x][y][z].west = false;
			}
			else
			{
				if(c.chunk[x + 1][y][z] == BlockType.AIR)
					c.visibility[x][y][z].west = true;
				else
					c.visibility[x][y][z].west = false;
			}
			
			//BOTTOM
			if(y == 0)
			{
				//bottom of world is never rendered
				c.visibility[x][y][z].bottom = false;
			}
			else
			{
				if(c.chunk[x][y - 1][z] == BlockType.AIR)
					c.visibility[x][y][z].bottom = true;
				else
					c.visibility[x][y][z].bottom = false;
			}
			
			//TOP
			if(y == c.chunk[0].length - 1)
			{
				//top blocks are always visible
				c.visibility[x][y][z].top = true;
			}
			else
			{
				if(c.chunk[x][y + 1][z] == BlockType.AIR)
					c.visibility[x][y][z].top = true;
				else
					c.visibility[x][y][z].top = false;
			}
			
			//NORTH
			if(z == c.chunk[0][0].length - 1)
			{
				//check visibility of nearby chunk
				if(World.chunkExists(c.offsetX, c.offsetZ + Chunk.depth))
				{
					adjacent = World.getChunk(c.offsetX, c.offsetZ + Chunk.depth);
					if(adjacent.chunk[x][y][0] == BlockType.AIR)
						c.visibility[x][y][z].north = true;
					else
						c.visibility[x][y][z].north = false;
				}
				else
					c.visibility[x][y][z].north = false;
			}
			else
			{
				if(c.chunk[x][y][z + 1] == BlockType.AIR)
					c.visibility[x][y][z].north = true;
				else
					c.visibility[x][y][z].north = false;
			}

			//SOUTH
			if(z == 0)
			{
				//check visibility of nearby chunk
				if(World.chunkExists(c.offsetX, c.offsetZ - Chunk.depth))
				{
					adjacent = World.getChunk(c.offsetX, c.offsetZ - Chunk.depth);
					if(adjacent.chunk[x][y][Chunk.depth - 1] == BlockType.AIR)
						c.visibility[x][y][z].south = true;
					else
						c.visibility[x][y][z].south = false;
				}
				else
					c.visibility[x][y][z].south = false;
			}
			else
			{
				if(c.chunk[x][y][z - 1] == BlockType.AIR)
					c.visibility[x][y][z].south = true;
				else
					c.visibility[x][y][z].south = false;
			}
		}
	}
	
	//for this check y top and bottom. If out of bounds return false.  If out of bounds of the current chunk, switch chunk and
	//update coordinates.  This can only convert to adjacent chunks.
	public static boolean checkBounds(Chunk parent, int x, int y, int z)
	{
		if(y >= Chunk.height)
			return false;
		else if(y < 0)
			return false;
		if(x >= Chunk.width)
		{
			parent = World.getChunk(x + Chunk.width, z);
			x = x - Chunk.width;
		}
		else if(x < 0)
		{
			parent = World.getChunk(x - Chunk.width, z);
			x = x + Chunk.width;
		}
		if(z >= Chunk.depth)
		{
			parent = World.getChunk(x, z + Chunk.depth);
			z = z + Chunk.depth;
		}
		else if(z < 0)
		{
			parent = World.getChunk(x, z + Chunk.depth);
			z = z - Chunk.depth;
		}
		return true;
	}
	
	public static void drawBlock(Chunk parent, int x, int y, int z)
	{
		//block is only drawn if the coordinates requested are valid
		if(checkBounds(parent, x, y, z))
		{
			Quad quad = new Quad(blockSize, blockSize);
			//draw north face
			if(parent.visibility[x][y][z].north && Engine.getEngine().getWorld().getWorldNode().getChild(parent.offsetX + "^" + parent.offsetZ + ":" + x + "-" + y + "-" + z + "N") == null)
			{
				//Geometry
				Geometry block = new Geometry(parent.offsetX + "^" + parent.offsetZ + ":" + x + "-" + y + "-" + z + "N", quad);
				block.setMaterial(Materials.getMaterial((int)parent.chunk[x][y][z]));
				block.setLocalTranslation(new Vector3f(x + parent.offsetX, y, z + blockSize + parent.offsetZ));
				block.setUserData("x", x);
				block.setUserData("y", y);
				block.setUserData("z", z);
				block.setUserData("chunk", parent.offsetX + "^" + parent.offsetZ);
				block.setUserData("face", "north");
				Engine.getEngine().getWorld().getWorldNode().attachChild(block);
				
				//Physics
			    if(physicsEnabled)
			    {
			    	block.addControl(new RigidBodyControl(0f));
				    Engine.getEngine().getStateManager().getState(BulletAppState.class).getPhysicsSpace().add(block);
			    }
			}
			
			//draw south face
			if(parent.visibility[x][y][z].south && Engine.getEngine().getWorld().getWorldNode().getChild(parent.offsetX + "^" + parent.offsetZ + ":" + x + "-" + y + "-" + z + "S") == null)
			{
				//Geometry
				Quaternion q = new Quaternion();
				q.fromAngleAxis(FastMath.PI, new Vector3f(0, 1, 0));
				
				Geometry block = new Geometry(parent.offsetX + "^" + parent.offsetZ + ":" + x + "-" + y + "-" + z + "S", quad);
				block.setMaterial(Materials.getMaterial((int)parent.chunk[x][y][z]));
				block.setLocalTranslation(new Vector3f(x + blockSize + parent.offsetX, y, z + parent.offsetZ));
				block.setLocalRotation(q);
				block.setUserData("x", x);
				block.setUserData("y", y);
				block.setUserData("z", z);
				block.setUserData("chunk", parent.offsetX + "^" + parent.offsetZ);
				block.setUserData("face", "south");
				Engine.getEngine().getWorld().getWorldNode().attachChild(block);
				
				//Physics
				if(physicsEnabled)
				{
					block.addControl(new RigidBodyControl(0f));
				    Engine.getEngine().getStateManager().getState(BulletAppState.class).getPhysicsSpace().add(block);
				}
			}
			
			//draw east face
			if(parent.visibility[x][y][z].east && Engine.getEngine().getWorld().getWorldNode().getChild(parent.offsetX + "^" + parent.offsetZ + ":" + x + "-" + y + "-" + z + "E") == null)
			{
				//Geometry
				Quaternion q = new Quaternion();
				q.fromAngleAxis(-FastMath.PI/2, new Vector3f(0, 1, 0));
				
				Geometry block = new Geometry(parent.offsetX + "^" + parent.offsetZ + ":" + x + "-" + y + "-" + z + "E", quad);
				block.setMaterial(Materials.getMaterial((int)parent.chunk[x][y][z]));
				block.setLocalTranslation(new Vector3f(x + parent.offsetX, y, z + parent.offsetZ));
				block.setLocalRotation(q);
				block.setLocalRotation(q);
				block.setUserData("x", x);
				block.setUserData("y", y);
				block.setUserData("z", z);
				block.setUserData("chunk", parent.offsetX + "^" + parent.offsetZ);
				block.setUserData("face", "east");
				Engine.getEngine().getWorld().getWorldNode().attachChild(block);
				
				//Physics
			    if(physicsEnabled)
			    {
			    	block.addControl(new RigidBodyControl(0f));
				    Engine.getEngine().getStateManager().getState(BulletAppState.class).getPhysicsSpace().add(block);
			    }
			}
			
			//draw west face
			if(parent.visibility[x][y][z].west && Engine.getEngine().getWorld().getWorldNode().getChild(parent.offsetX + "^" + parent.offsetZ + ":" + x + "-" + y + "-" + z + "W") == null)
			{
				//Geometry
				Quaternion q = new Quaternion();
				q.fromAngleAxis(FastMath.PI/2, new Vector3f(0, 1, 0));
				
				Geometry block = new Geometry(parent.offsetX + "^" + parent.offsetZ + ":" + x + "-" + y + "-" + z + "W", quad);
				block.setMaterial(Materials.getMaterial((int)parent.chunk[x][y][z]));
				block.setLocalTranslation(new Vector3f(x + blockSize + parent.offsetX, y, z + blockSize + parent.offsetZ));
				block.setLocalRotation(q);
				block.setUserData("x", x);
				block.setUserData("y", y);
				block.setUserData("z", z);
				block.setUserData("chunk", parent.offsetX + "^" + parent.offsetZ);
				block.setUserData("face", "west");
				Engine.getEngine().getWorld().getWorldNode().attachChild(block);
				
				//Physics
			    if(physicsEnabled)
			    {
			    	block.addControl(new RigidBodyControl(0f));
				    Engine.getEngine().getStateManager().getState(BulletAppState.class).getPhysicsSpace().add(block);
			    }
			}
			
			//draw top face
			if(parent.visibility[x][y][z].top && Engine.getEngine().getWorld().getWorldNode().getChild(parent.offsetX + "^" + parent.offsetZ + ":" + x + "-" + y + "-" + z + "T") == null)
			{
				//Geometry
				
				Quaternion q = new Quaternion();
				q.fromAngleAxis(3 * FastMath.PI / 2, new Vector3f(1, 0, 0));
				
				Geometry block = new Geometry(parent.offsetX + "^" + parent.offsetZ + ":" + x + "-" + y + "-" + z + "T", quad);
				block.setMaterial(Materials.getMaterial((int)parent.chunk[x][y][z]));
				block.setLocalTranslation(new Vector3f(x + parent.offsetX, y + blockSize, z + blockSize + parent.offsetZ));
				block.setLocalRotation(q);
				block.setLocalRotation(q);
				block.setUserData("x", x);
				block.setUserData("y", y);
				block.setUserData("z", z);
				block.setUserData("chunk", parent.offsetX + "^" + parent.offsetZ);
				block.setUserData("face", "top");
				Engine.getEngine().getWorld().getWorldNode().attachChild(block);
				
				//Physics
			    if(physicsEnabled)
			    {
			    	block.addControl(new RigidBodyControl(0f));
				    Engine.getEngine().getStateManager().getState(BulletAppState.class).getPhysicsSpace().add(block);
			    }
			}
			if(parent.visibility[x][y][z].bottom && Engine.getEngine().getWorld().getWorldNode().getChild(parent.offsetX + "^" + parent.offsetZ + ":" + x + "-" + y + "-" + z + "B") == null)
			{
				//Geometry
				
				Quaternion quatX = new Quaternion();
				quatX.fromAngleAxis(FastMath.PI/2, new Vector3f(1, 0, 0));
				
				Geometry block = new Geometry(parent.offsetX + "^" + parent.offsetZ + ":" + x + "-" + y + "-" + z + "B", quad);
				block.setMaterial(Materials.getMaterial((int)parent.chunk[x][y][z]));
				block.setLocalTranslation(new Vector3f(x + parent.offsetX, y, z + parent.offsetZ));
				block.setLocalRotation(quatX);
				block.setUserData("x", x);
				block.setUserData("y", y);
				block.setUserData("z", z);
				block.setUserData("chunk", parent.offsetX + "^" + parent.offsetZ);
				block.setUserData("face", "bottom");
				Engine.getEngine().getWorld().getWorldNode().attachChild(block);
				
				//Physics
			    if(physicsEnabled)
			    {
			    	block.addControl(new RigidBodyControl(0f));
				    Engine.getEngine().getStateManager().getState(BulletAppState.class).getPhysicsSpace().add(block);
			    }
			}
		}
	}
}
